﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Xml;
using System.Web.Mvc;
using System.Collections;
using System.Configuration;
using System.Data;
using DataAnnotationsExtensions;

namespace MobiShop.Models
{
    [MetadataType(typeof(OrderMetaData))]
    public partial class Order { }

    public class OrderMetaData
    {
        [Display(Name = "Tên người nhận:")]
        [Required(ErrorMessage = "Tên người nhận hàng phải khác trống")]
        [RegularExpression(@"^[\D]*$", ErrorMessage = "Tên người nhận không được chứa ký tự số")]
        [StringLength(200, MinimumLength = 3, ErrorMessage = "Tên người nhận hàng phải có ít nhất 3 ký tự")]
        public string receiveName { get; set; }


        [Display(Name = "Địa chỉ nhận:")]
        [Required(ErrorMessage = "Địa chỉ nhận hàng phải khác trống")]
        [StringLength(500, MinimumLength = 5, ErrorMessage = "Địa chỉ phải có độ dài từ 5 đến 500 ký tự")]
        [DataType(DataType.MultilineText)]
        public string receiveAddress { get; set; }


        [Display(Name = "Điện thoại:")]
        [StringLength(15, MinimumLength = 6, ErrorMessage = "Số điện thoại phải từ 6 đến 15 ký tự")]
        [DataType(DataType.PhoneNumber, ErrorMessage = "Số điện thoại phải là 1 dãy số hợp lệ.VD: 0986754322")]
        [RegularExpression(@"^[\d- ]*[\d]{3,}[\d- ]*$", ErrorMessage = "Số điện thoại phải là 1 dãy số hợp lệ.VD: 0986754322")]
        public string receivePhoneNumber { get; set; }


        [Display(Name = "Số CMND:")]
        [StringLength(15, MinimumLength = 6, ErrorMessage = "Số CMND phải từ 6 đến 15 ký tự")]
        [RegularExpression(@"^\s*\d{6,}\s*$", ErrorMessage = "Số CMND phải là 1 dãy số liên tiếp từ 6 ký tự trở lên")]
        public string receiveIdentityCardNumber { get; set; }

        [Display(Name = "Loại thẻ:")]
        public int? cardType { get; set; }


        [Display(Name = "Mã thẻ:")]
        [Required(ErrorMessage = "Mã thẻ tín dụng phải khác trống")]
        [StringLength(20, MinimumLength = 15, ErrorMessage = "Mã thẻ phải là 15 hoặc 16 chữ số viết liền nhau")]
        [RegularExpression(@"^(\s*\d{15,16}\s*|\d{4}X{11,12})$", ErrorMessage = "Mã thẻ phải là 15 hoặc 16 chữ số viết liền nhau")]
        public string creditCardNumber { get; set; }


        [Display(Name = "Mã CVV:")]
        [Required(ErrorMessage = "Mã bảo mật CVV phải khác trống")]
        [StringLength(4, MinimumLength = 3, ErrorMessage = "Mã CVV phải là 3 hoặc 4 chữ số viết liền nhau")]
        [RegularExpression(@"^(\s*\d{3,4}\s*|XXXX|XXX)$", ErrorMessage = "Mã CVV phải là 3 hoặc 4 chữ số viết liền nhau")]
        public string cardCVV { get; set; }


        [Display(Name = "")]
        public int? cardMonthExpires { get; set; }


        [Display(Name = "")]
        public int? cardYearExpires { get; set; }




        [Display(Name = "Trạng thái:")]
        [Integer(ErrorMessage = "Mã đơn hàng phải là giá trị nguyên hợp lệ")]
        public int? orderStatusId { get; set; }

        [Display(Name = "Ngày nhận hàng:")]
        [Date(ErrorMessage = "Ngày nhận hàng phải là ngày hợp lệ")]
        [DisplayFormat(ApplyFormatInEditMode=true, DataFormatString="{0:d MMM yyyy}")]
        public DateTime? receiveDate { get; set; }

        [Display(Name = "Tình trạng:")]
        public bool? visible { get; set; }
    }

    public class OrderModels
    {
        #region "thuộc tính và các hàm static"
        public static List<SelectListItem> CardTypeList = ReadCardTypeList();
        public static List<SelectListItem> MonthList = CreateMonthList();
        public static List<SelectListItem> YearList = CreateYearList();

        private static List<SelectListItem> ReadCardTypeList()
        {
            XmlDocument document = new XmlDocument();
            document.LoadXml(CompanyModels.company.xmlCardType);
            XmlElement rootNode = document.DocumentElement;
            XmlNodeList nodeCardTypeList = rootNode.ChildNodes;

            List<SelectListItem> list = new List<SelectListItem>();
            SelectListItem item;
            foreach (XmlNode nodeCardType in nodeCardTypeList)
            {
                item = new SelectListItem();
                item.Text = nodeCardType.Attributes["name"].Value;
                item.Value = nodeCardType.Attributes["value"].Value;
                list.Add(item);
            }
            list[0].Selected = true;
            return list;
        }

        private static List<SelectListItem> CreateMonthList()
        {
            List<SelectListItem> list = new List<SelectListItem>();
            SelectListItem item;

            for (int i = 1; i <= 12; i++)
            {
                item = new SelectListItem();
                item.Text = i.ToString();
                item.Value = i.ToString();
                list.Add(item);
            }
            list[DateTime.Now.Month-1].Selected = true;
            return list;
        }

        private static List<SelectListItem> CreateYearList()
        {
            List<SelectListItem> list = new List<SelectListItem>();
            SelectListItem item;

            int yearNow = DateTime.Now.Year;
            for (int i = 0; i <= 6; i++)
            {
                item = new SelectListItem();
                item.Text = yearNow.ToString();
                item.Value = yearNow.ToString();
                list.Add(item);
                yearNow++;
            }
            list[0].Selected = true;
            return list;
        }
        #endregion

        #region "Các hàm xử lý"
        public static string PayOrder(Order order, Account account, ShoppingBasket basket)
        {
            MobishopEntities content = new MobishopEntities();
            content.Connection.Open();
            using (System.Data.Common.DbTransaction transaction = content.Connection.BeginTransaction(IsolationLevel.RepeatableRead))
            {
                try
                {
                    // đọc lại danh sách các sản phẩm trong giỏ hàng
                    Hashtable basketHash = basket.ListProduct;
                    List<int> listID = basket.ListProduct_ID;
                    List<Product> listProduct = content.ProductList.Where(product => listID.Contains(product.id) && product.visible==true).ToList();
                    
                    // tạo ra danh sách OrderDetail tương ứng với giỏ hàng
                    decimal totalPayment = 0;
                    List<OrderDetail> listOrderDetail = new List<OrderDetail>();
                    bool hasProduct = false;
                    foreach (Product product in listProduct)
                    {
                        OrderDetail orderDetail = new OrderDetail();
                        orderDetail.quantity = (int)basketHash[product.id];
                        if (orderDetail.quantity > 0) // chỉ thêm các mặt hàng có số lượng >0
                        {
                            if (product.totalQuantity < orderDetail.quantity)
                            {
                                transaction.Rollback();
                                return "Mặt hàng " + product.name + " không đủ số lượng cung ứng!";
                            }

                            orderDetail.price = product.price;
                            orderDetail.orderId = null;
                            orderDetail.productId = product.id;
                            orderDetail.visible = true;

                            totalPayment += (decimal)orderDetail.price * orderDetail.quantity;
                            listOrderDetail.Add(orderDetail);

                            // tiến hành trừ bớt số lượng hàng đã đặt
                            product.totalQuantity -= orderDetail.quantity;
                            // tăng số lượng hàng đã bán
                            product.totalSale += orderDetail.quantity;
                            hasProduct = true;
                        }
                    }
                    if (!hasProduct)
                    {
                        transaction.Rollback();
                        return "Hệ thống không chấp nhận thanh toán cho giỏ hàng trống!";
                    }

                    // bổ sung dữ liệu vào đối tượng order
                    order.orderDate = DateTime.Now;
                    order.totalPayment = totalPayment;
                    order.customerId = account.id;
                    order.orderStatusId = 1;
                    order.receiveDate = null;
                    order.visible = true;

                    content.OrderList.AddObject(order);
                    // tiến hành lưu thay đổi của các Product và order xuống CSDL
                    content.SaveChanges();

                    // tiến hành cập nhật lại orderID cho các OrderDetail, sau đó lưu xuống XSDL
                    foreach (OrderDetail orderDetail in listOrderDetail)
                    {
                        orderDetail.orderId = order.id;
                        content.OrderDetailList.AddObject(orderDetail);
                    }
                    content.SaveChanges();

                    // Thanh toán tiền tại service giả lập
                    string result = WithdrawMoneyFromBank(order);
                    if (result != "") // có lỗi
                    {
                        transaction.Rollback();
                        return result;
                    }

                    // ok, ko có lỗi => commit
                    transaction.Commit();
                    return "";
                }
                catch (System.Exception)
                {
                    transaction.Rollback();
                    return "Xảy ra lỗi khi thao tác CSDL!";
                }
                finally
                {
                    if (content.Connection.State == ConnectionState.Open)
                        content.Connection.Close();
                }
            }
        }

        // Thanh toán tiền tại service giả lập
        private static string WithdrawMoneyFromBank(Order order)
        {
            if (ShoppingBasket.useCreditCard)
            {
                if (order.cardType == null || order.cardMonthExpires == null ||
                    order.cardYearExpires == null || order.totalPayment == null)
                    return "Thông tin thẻ tín dụng không hợp lệ!";
                try
                {
                    BankService.Service bank = new BankService.Service();
                    int result = bank.Pay((int)order.cardType, order.creditCardNumber,
                        order.cardCVV, (int)order.cardMonthExpires, (int)order.cardYearExpires, 
                        (decimal)order.totalPayment);
                    switch (result)
                    {
                        case 0:
                            return "";
                        case 1:
                            return "Không tìm thấy thẻ có thông tin như bạn cung cấp !";
                        case 2:
                            return "Thẻ tín dụng của bạn không có đủ tiền để thực hiện giao dịch này.";
                        case 3:
                            return "Bank service bị lỗi, thanh toán bị từ chối.";
                        default:
                            return "Bank service bị lỗi, thanh toán bị từ chối.";
                    }
                }
                catch (System.Exception)
                {
                    return "Kết nối ngân hàng bị lỗi!";
                }
            }
            return "";
        }

        public static bool ChangeVisibleStatus(int OrderId)
        {
            bool kq=false;
            if (OrderId > 0)
            {
                MobishopEntities db = new MobishopEntities();
                try
                {
                    Order order = db.OrderList.Single(o => o.id == OrderId);
                    if (order.visible == true)
                        order.visible = false;
                    else order.visible = true;
                    db.SaveChanges();
                    kq = true; 
                }
                catch (System.Exception)
                {}
            }
            return kq;
        }

        // Nếu ở thao tác này, đơn hàng được chuyển sang trạng thái 3 ( đã hoàn tất ) thì receiveDate
        // sẽ tự động đc set bằng now, trả về dateChange=true
        // => chuỗi xml trả về sẽ có dạng <result receiveDate="1 March 2001 3:40:00 PM" />
        // nếu chuyển sang trạng thái 4 ( bị hủy ) thì receiveDate = null, dateChange=true
        // => <result receiveDate="" />
        public static Order ChangeOrderStatusId(int OrderId, int? orderStatusId, out bool dateChange)
        {
            dateChange = false;
            if (OrderId > 0 && orderStatusId>=0)
            {
                if (orderStatusId == 0) orderStatusId = null;
                MobishopEntities db = new MobishopEntities();
                try
                {
                    Order order = db.OrderList.Single(o => o.id == OrderId);
                    // ko xử lý, trả về null nếu ko có thay đổi gì xảy ra với orderStatusId
                    if (order.orderStatusId != orderStatusId) 
                    {
                        order.orderStatusId = orderStatusId;
                        if (orderStatusId == 3)
                        {
                            order.receiveDate = DateTime.Now;
                            dateChange = true;
                        }
                        else if (orderStatusId == 4)
                        {
                            order.receiveDate = null;
                            dateChange = true;
                        }
                        db.SaveChanges();
                        return order; // chỉ trả về order nếu thực sự có thay đổi xảy ra trên CSDL
                    }
                }
                catch (System.Exception)
                { }
            }
            return null;
        }

        #endregion
    }
}